#include "Parser.h"

#include "Calculator.h"

#include <stdio.h>
#include <string.h>
#include <sstream>
#include <iostream>

using namespace std;


//------------------------------------------------------------------------------

Parser::Parser() {
	function = "";
	pos = 0;
}

//------------------------------------------------------------------------------

Parser::Parser( string f ) {
	function = f;
	removeSpaces();
	removeSpaces();
	pos = 0;
}

//------------------------------------------------------------------------------

Parser::~Parser() {
}

//------------------------------------------------------------------------------

double Parser::calculate(string function) {
	double rec = 0;
	double a;
	int* length = new int;
	if( function[pos] == '(' ) {
		pos++;
		a = calculate(function);
	} else {
		if( isdigit(function[pos]) || function[pos] == '-' || (function[pos] == ' ' && function[pos+1] != '(' )) {
			a = readNumber( function.c_str()+pos, length );
			pos += *length;
		} else {
			double* v = new double;
			string f = readFunction( function, v );
			a = Calculator::calculate( f.c_str(), *v );
		}
	}

	if( pos >= function.size() || function[pos] == ')' ) {
		pos++;
		return a;
	}

	char operation = function[pos];
	int prioritaireA = Calculator::getPrioritaire(operation);

	if( operation == '(' || (operation >= 65 && operation <= 122 && operation != 94 ))
		pos--;

	pos++;
	int dupa = pos;
	double b;
	if( function[pos] == '(' ) {
		pos++;
		b = calculate(function);
	} else if( function[pos] == ' ' && function[pos+1] == '(' ) {
		pos++;
		pos++;
		b = calculate(function);
	} else {
		if( isdigit(function[pos]) || function[pos] == '-' || (function[pos] == ' ' && function[pos+1] != '(' ) ) {
			b = readNumber( function.c_str()+pos, length );
			pos += *length;
		} else {
			double* v = new double;
			string f = readFunction( function, v );
			b = Calculator::calculate( f.c_str(), *v );
		}
	}

	if( pos >= function.size() || function[pos] == ')' ) {
		pos++;
		return Calculator::calculate( a, b, operation );
	}

	char operation2 = function[pos];
	int prioritaireB = Calculator::getPrioritaire(operation2);

	if( operation2 == '(' || (operation2 >= 65 && operation2 <= 122 && operation != 94 ))
		pos--;

	pos++;
	double dupa2 = pos;
	rec = a;
	while( prioritaireB <= prioritaireA ) {
		rec = Calculator::calculate( rec, b, operation );

		if( function[pos] == '(' ) {
			pos++;
			b = calculate(function);
		} else if( function[pos] == ' ' && function[pos+1] == '(' ) {
			pos++;
			pos++;
			b = calculate(function);
		} else {
			if( isdigit(function[pos]) || function[pos] == '-' || (function[pos] == ' ' && function[pos+1] != '(' ) ) {
				b = readNumber( function.c_str()+pos, length );
				pos += *length;
			} else {
				double* v = new double;
				string f = readFunction( function, v );
				b = Calculator::calculate( f.c_str(), *v );
			}
		}

		if( pos >= function.size() || function[pos] == ')' ) {
			pos++;
			return Calculator::calculate( rec, b, operation2 );
		}

		operation = operation2;
		operation2 = function[pos];
		prioritaireA = prioritaireB;
		prioritaireB = Calculator::getPrioritaire(operation2);

		if( operation2 == '(' || (operation2 >= 65 && operation2 <= 122 && operation != 94  ))
			pos--;

		pos++;
		dupa = dupa2;
		dupa2 = pos;
	}
	pos = dupa;
	if( operation == '-')
		Calculator::change();

	double bb = calculate(function);

	if( operation == '-')
			Calculator::change();
	return Calculator::calculate( rec, bb, operation );
}

//------------------------------------------------------------------------------

vector<double> Parser::getY( vector<double> x ) {
	vector<double> y;

	for( int i = 0; i < x.size(); i++ ) {
		ostringstream ss;
		ss << "(" << x[i] << ")";

		string var = strReplace( function, string("x"), ss.str() );

		pos = 0;
		y.push_back(calculate(var));
	}

	return y;
}

//------------------------------------------------------------------------------

void Parser::removeSpaces() {
	for( int i = 0; i < function.size(); i++ ) {
		if( function[i] == ' ' ) {
			function.erase(i,1);
			i--;
		}
	}
}

//------------------------------------------------------------------------------

double Parser::readNumber( const char* start, int* length ) {
	int size = strlen(start);
	if( strlen(start) == 0 )
		return 0.0;
	int sign = 1;
	int i = 0;

	while( start[i] == ' ' ) {
		i++;
	}

	while( start[i] == '-' ) {
		i++;
		sign = -1;
	}

	double rec = 0;
	double p = 1.0;
	bool dot = false;
	while( i < size && isdigit(start[i]) ) {
		rec = rec*p+(start[i]-48);
		p *= 10;
		i++;
		if( start[i] == '.' ) {
			dot = true;
			break;
		}
	}

	if( dot == false ) {
		*length = i;
		return sign*rec;
	}
	i++;
	p = 0.1;
	while( i < size && isdigit(start[i]) ) {
		rec = rec + (start[i]-48)*p;
		p /= 10;
		i++;
	}
	*length = i;
	return sign*rec;
}

//------------------------------------------------------------------------------

string Parser::readFunction( string function, double* v ) {
	string f = "";
	while( function[pos] != '(' && pos <= function.size() ) {
		f.push_back(function[pos]);
		pos++;
	}

	pos++;

	*v = calculate( function );

	return f;
}

//------------------------------------------------------------------------------

int Parser::getLength( double n ) {
	char buffer[100];
	sprintf( buffer, "%.2f", (double)n );
	return strlen(buffer);
}

//------------------------------------------------------------------------------

vector<double> Parser::generateX( double from, double to, double step ) {
	int n = (to - from) / step;
	vector<double> x;

	if( n > 0 ) {
		x.push_back(from);
		for( int i = 0; i <= n; i++ ) {
			x.push_back(x.at(i)+step);
		}
	}

	if( from < 0 && to > 0 ) {
		x[from/step*-1+1] = 0.0;
		if( n%2 == 0 )
			x[from/step*-1] = 0.0;
	}

	return x;
}

//------------------------------------------------------------------------------

string Parser::strReplace( string s, string searchString, string replaceString ) {
    string str = s;

	string::size_type pos = 0;
    while ( (pos = str.find(searchString, pos)) != string::npos ) {
        str.replace( pos, searchString.size(), replaceString );
        pos++;
    }

    return str;
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------

vector<double> Parser::getDiff( vector<double> y ) {
	vector<double> diff;

	for( int i = 0; i < y.size()-1; i++ ) {
		diff.push_back( y[i+1] - y[i] );
	}

	return diff;
}

//------------------------------------------------------------------------------
